package service

import (
	"errors"
	"fmt"
	"gorm.io/gorm"
	"seat-service/initialization"
	"seat-service/model"
	"seat-service/model/dto"
	"seat-service/utils"
	"strconv"
	"time"
)

type BuildingsAdminService struct {
}

var (
	classroomAdmin = ClassroomAdminService{}
	seatService    = SeatService{}
)

func (b *BuildingsAdminService) SelectBuildingsByCondition(conditions dto.AdminQueryBuildCondition) (count int64, buildingInformations []dto.BuildInformation, err error) {
	db := initialization.DB.Model(&model.Building{}) // 使用Clone创建一个新的DB实例
	if conditions.BuildName != "" {
		db = db.Where("name like ?", "%"+conditions.BuildName+"%")
	}
	if conditions.BuildStatus != -1 {
		db = db.Where("building_status = ?", conditions.BuildStatus)
	}
	if conditions.LockStartTime != "" || len(conditions.LockStartTime) != 0 {
		//将时间戳转化为正确的时间格式
		timeLockStr := conditions.LockStartTime
		// 将字符串时间戳转换为整数
		timestamp, _ := strconv.ParseInt(timeLockStr, 10, 64)
		// 使用 time.Unix 将时间戳转换为 time.Time 类型
		timeLock := time.Unix(timestamp, 0)
		// 将时间格式化为特定格式的时间字符串
		formattedTimeLock := timeLock.Format("2006-01-02 15:04:05")
		db = db.Where("lock_start_time = ?", formattedTimeLock)
	}
	if conditions.LockEndTime != "" || len(conditions.LockEndTime) != 0 {
		//将时间戳转化为正确的时间格式
		timeLockStr := conditions.LockEndTime
		// 将字符串时间戳转换为整数
		timestamp, _ := strconv.ParseInt(timeLockStr, 10, 64)
		// 使用 time.Unix 将时间戳转换为 time.Time 类型
		timeLock := time.Unix(timestamp, 0)
		// 将时间格式化为特定格式的时间字符串
		formattedTimeLock := timeLock.Format("2006-01-02 15:04:05")
		db = db.Where("lock_end_time = ?", formattedTimeLock)
	}
	offset := (conditions.PageNo - 1) * conditions.PageSize
	db = db.Joins("LEFT JOIN campus AS c ON buildings.campus_id = c.id").
		Select("buildings.*, c.campus_name AS campus_name")
	err = db.Debug().Count(&count).Offset(offset).Limit(conditions.PageSize).Scan(&buildingInformations).Error
	return
}

func (b *BuildingsAdminService) AddBuilding(newBuildInformation dto.NewBuildInformation) (err error) {
	//根据校区名称查询出校区id
	campusId, err := b.SelectCampusIdByName(newBuildInformation.CampusName)
	if err != nil {
		return
	}
	if campusId == 0 {
		err = fmt.Errorf("CampusIsNotExist")
		return
	}
	newBuild := model.Building{
		Name:           newBuildInformation.Name,
		CampusId:       uint(campusId),
		BuildingStatus: newBuildInformation.BuildingStatus,
		HeadImg:        newBuildInformation.HeadImg,
		Longitude:      newBuildInformation.Longitude, //经度
		Latitude:       newBuildInformation.Latitude,  //纬度
	}

	var lockStartTime, lockEndTime *time.Time // 使用指针类型以便存储 nil

	if newBuildInformation.LockStartTime != "" {
		timestamp, err := strconv.ParseInt(newBuildInformation.LockStartTime, 10, 64)
		if err != nil {
			return err
		}
		t := time.Unix(timestamp, 0)
		lockStartTime = &t
	}

	if newBuildInformation.LockEndTime != "" {
		timestamp, err := strconv.ParseInt(newBuildInformation.LockEndTime, 10, 64)
		if err != nil {
			return err
		}
		t := time.Unix(timestamp, 0)
		lockEndTime = &t
	}
	//判断楼栋是否存在
	if err = initialization.DB.
		Where("name = ? and campus_id = ?", newBuild.Name, newBuild.CampusId).
		First(&model.Building{}).Error; err != nil {
		newBuild.LockStartTime = lockStartTime
		newBuild.LockEndTime = lockEndTime
		// 单个插入
		err = initialization.DB.Create(&newBuild).Error
		//维护redis中的缓存
		buildData := map[string]string{
			strconv.Itoa(int(newBuild.ID)): newBuildInformation.Name,
		}
		buildId := map[string]string{
			strconv.Itoa(int(newBuild.ID)): strconv.Itoa(campusId),
		}
		err = utils.CacheRedisData(utils.Building, buildData) //增加楼栋数据
		err = utils.CacheRedisID(utils.Building, buildId)     //增加校区 楼栋中间表的数据
		return err
	}
	return errors.New("BuildExists")
}

func (b *BuildingsAdminService) SelectBuildById(id int) (buildInformation dto.BuildInformation, err error) {
	err = initialization.DB.Model(&model.Building{}).Where("buildings.id = ?", id).Joins("LEFT JOIN campus AS c ON buildings.campus_id = c.id").Select("buildings.* , c.campus_name AS campus_name").Scan(&buildInformation).Error
	return
}

func (b *BuildingsAdminService) UpdateBuildData(building dto.UpdateBuildParams) (newBuildInformation dto.BuildInformation, err error) {
	newBuild := model.Building{
		Name:           building.Name,
		CampusId:       building.CampusId,
		BuildingStatus: building.BuildingStatus,
		HeadImg:        building.HeadImg,
		Longitude:      building.Longitude, //经度
		Latitude:       building.Latitude,  //纬度
	}
	//转化时间格式
	var lockStartTime, lockEndTime *time.Time // 使用指针类型以便存储 nil
	if building.LockStartTime != "" {
		timestamp, _ := strconv.ParseInt(building.LockStartTime, 10, 64)
		t := time.Unix(timestamp, 0)
		lockStartTime = &t
	}
	if building.LockEndTime != "" {
		timestamp, _ := strconv.ParseInt(building.LockEndTime, 10, 64)
		t := time.Unix(timestamp, 0)
		lockEndTime = &t
	}
	newBuild.LockStartTime = lockStartTime
	newBuild.LockEndTime = lockEndTime
	//修改信息
	updateBuild := map[string]interface{}{
		"name":            newBuild.Name,
		"lock_start_time": newBuild.LockStartTime,
		"lock_end_time":   newBuild.LockEndTime,
		"campus_id":       newBuild.CampusId,
		"building_status": newBuild.BuildingStatus,
		"head_img":        newBuild.HeadImg,
		"longitude":       newBuild.Longitude,
		"latitude":        newBuild.Latitude,
	}
	if newBuild.BuildingStatus == 0 {
		updateBuild["building_status"] = newBuild.BuildingStatus
	}
	if newBuild.LockStartTime == nil {
		updateBuild["lock_start_time"] = newBuild.LockStartTime
	}
	if newBuild.LockEndTime == nil {
		updateBuild["lock_end_time"] = newBuild.LockEndTime
	}
	err = initialization.DB.Model(&model.Building{}).Where("id = ?", building.BuildId).Updates(&updateBuild).Error
	if err != nil {
		return
	}
	//根据楼栋id 查询出新的信息
	newBuildInformation, err = b.SelectBuildById(building.BuildId)
	//修改的同时更新redis中的缓存
	buildData := map[string]string{
		strconv.Itoa(building.BuildId): newBuild.Name,
	}
	buildId := map[string]string{
		strconv.Itoa(building.BuildId): strconv.Itoa(int(building.CampusId)),
	}
	err = utils.CacheRedisData(utils.Building, buildData) //更新楼栋名称
	err = utils.CacheRedisID(utils.Building, buildId)     //更新楼栋所属的校区id
	return
}

func (b *BuildingsAdminService) DeleteByIds(ids dto.DeleteIds) (err error) {
	// 删除redis中的缓存
	fieldsToDelete := make([]string, 0, len(ids.BuildIds))
	// 整型转字符串
	for _, value := range ids.BuildIds {
		field := strconv.Itoa(value) // redis 数据库中map集合中的元素的key
		fieldsToDelete = append(fieldsToDelete, field)
	}
	// 删除build-data 的map集合中的元素
	err = utils.DeleteMapKey(utils.BuildingData, fieldsToDelete)
	if err != nil {
		return
	}
	// 删除build-id 的map集合中的楼栋
	err = utils.DeleteMapKey(utils.BuildingId, fieldsToDelete)
	if err != nil {
		return
	}

	// 开启事务
	tx := initialization.DB.Begin()
	defer func() {
		if r := recover(); r != nil {
			// 发生 panic 时回滚事务并释放连接
			tx.Rollback()
			panic(r)
		} else if err != nil {
			// 发生错误时回滚事务并释放连接
			tx.Rollback()
		}
	}()

	// 删除建筑记录
	err = tx.Where("id in (?)", ids.BuildIds).Delete(&model.Building{}).Error
	if err != nil {
		return
	}

	// 调用其他服务的事务删除方法
	err = classroomAdmin.DeleteByBuildingIDsWithTransaction(tx, ids.BuildIds)
	if err != nil {
		return
	}

	err = seatService.DeleteByBuildingIDsWithTransaction(tx, ids.BuildIds)
	if err != nil {
		return
	}

	// 事务提交后会自动释放连接
	err = tx.Commit().Error
	return
}

func (b *BuildingsAdminService) SelectAllCampus() (allCampus []model.Campus, err error) {
	err = initialization.DB.Find(&allCampus).Error
	return
}

func (b *BuildingsAdminService) SelectCampusIdByName(campusName string) (campusId int, err error) {
	err = initialization.DB.Model(model.Campus{}).Select("id").Where("campus_name = ?", campusName).Scan(&campusId).Error
	return
}

func (b *BuildingsAdminService) InsertExcelBuildInformations(builds []model.Building) (errBuilds []model.Building, err error) {
	//遍历数据并插入数据库 或者更新数据
	for _, buildInformation := range builds {
		var existBuild model.Building
		var idStr string
		//检查数据是否存在在数据库中
		result := initialization.DB.Where("name = ? and campus_id = ?", buildInformation.Name, buildInformation.CampusId).First(&existBuild)
		if result.Error != nil {
			switch {
			case errors.Is(result.Error, gorm.ErrRecordNotFound):
				//数据不存在 , 插入
				if err := initialization.DB.Create(&buildInformation).Error; err != nil {
					errBuilds = append(errBuilds, buildInformation)
					continue
				}
				//拿到插入的最后一条记录
				var newBuild model.Building
				initialization.DB.Last(&newBuild)
				idStr = strconv.Itoa(int(newBuild.ID))
			default:
				errBuilds = append(errBuilds, buildInformation)
			}

		} else {
			//数据存在 , 执行更新
			updateBuild := map[string]interface{}{
				"name":            buildInformation.Name,
				"lock_start_time": buildInformation.LockStartTime,
				"lock_end_time":   buildInformation.LockEndTime,
				"campus_id":       buildInformation.CampusId,
				"building_status": buildInformation.BuildingStatus,
			}
			if buildInformation.BuildingStatus == 0 {
				updateBuild["building_status"] = buildInformation.BuildingStatus
			}
			if buildInformation.LockStartTime == nil {
				updateBuild["lock_start_time"] = buildInformation.LockStartTime
			}
			if buildInformation.LockEndTime == nil {
				updateBuild["lock_end_time"] = buildInformation.LockEndTime
			}
			if err := initialization.DB.Debug().Model(&existBuild).Updates(&updateBuild).Error; err != nil {
				errBuilds = append(errBuilds, buildInformation)
			}
			idStr = strconv.Itoa(int(existBuild.ID))
		}
		//更新redis中的缓存信息 , 如果存在根据key 更新value , 如果不存在 , 插入数据
		data := map[string]string{
			idStr: buildInformation.Name,
		}
		if err := utils.CacheRedisData(utils.Building, data); err != nil {
			errBuilds = append(errBuilds, buildInformation)
		}
		idData := map[string]string{
			idStr: strconv.Itoa(int(buildInformation.CampusId)),
		}
		if err := utils.CacheRedisID(utils.Building, idData); err != nil {
			errBuilds = append(errBuilds, buildInformation)
		}
	}
	return
}
